package cn.jbolt.core.enumutil;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.EnumUtil;
import cn.jbolt.core.bean.Option;
import cn.jbolt.core.bean.OptionBean;
import com.jfinal.kit.StrKit;
import com.jfinal.template.Engine;

import java.util.*;
import java.util.stream.Collectors;

/**
 * JBolt 枚举类使用工具类
 *
 * @ClassName: JBoltEnum
 * @author: JFinal学院-小木 QQ：909854136
 * @date: 2020年3月17日
 */
public class JBoltEnum {
	// 存放 name为key的map Enum是值的
	private static HashMap<String, LinkedHashMap<String, ? extends Enum<?>>> ENUM_NAME_OBJECTMAP = new HashMap<String, LinkedHashMap<String, ? extends Enum<?>>>();
	// 存放 value为key的map Enum是值的
	private static HashMap<String, LinkedHashMap<String, ? extends Enum<?>>> ENUM_V_OBJECTMAP = new HashMap<String, LinkedHashMap<String, ? extends Enum<?>>>();
	// 存放 value和text 分别为key的map
	private static HashMap<String, LinkedHashMap<String, JBoltEnumBean>> ENUM_TV_BEANMAP = new HashMap<String, LinkedHashMap<String, JBoltEnumBean>>();
	// 存放 name 分别为key的map
	private static HashMap<String, LinkedHashMap<String, JBoltEnumBean>> ENUM_NAME_BEANMAP = new HashMap<String, LinkedHashMap<String, JBoltEnumBean>>();
	// 存放模板里的名 对应 class
	private static HashMap<String, Class<? extends Enum<?>>> ENUM_NAME_CLASSMAP = new HashMap<String, Class<? extends Enum<?>>>();

	/**
	 * 将枚举类 处理成map
	 *
	 * @param clazz
	 * @return
	 */
	public static <E extends Enum<E>> LinkedHashMap<String, JBoltEnumBean> addToTvBeanMap(Class<E> clazz) {
		return addToTvBeanMap(clazz, "text", "value");
	}

	/**
	 * 将枚举类 处理成map
	 *
	 * @param clazz
	 * @param textAttrName
	 * @return
	 */
	public static <E extends Enum<E>> LinkedHashMap<String, JBoltEnumBean> addToTvBeanMap(Class<E> clazz,String textAttrName) {
		return addToTvBeanMap(clazz, "text", null);
	}

	/**
	 * 添加到text和value作为key的map中管理
	 *
	 * @param clazz
	 * @param textAttrName
	 * @param valueAttrName
	 * @return
	 */
	public static <E extends Enum<E>> LinkedHashMap<String, JBoltEnumBean> addToTvBeanMap(Class<E> clazz,
			String textAttrName, String valueAttrName) {
		List<Object> texts = EnumUtil.getFieldValues(clazz, textAttrName);
		boolean hasValueAttr = valueAttrName!=null&&valueAttrName.trim().length()>0;
		List<String> names = null;
		List<Object> values = null;
		if(hasValueAttr){
			values = EnumUtil.getFieldValues(clazz, valueAttrName);
		}else{
			names = EnumUtil.getNames(clazz);
		}
		if (CollUtil.isEmpty(texts)) {
			return EMPTY_BEAN_HASHMAP;
		}
		// 组合beanMap
		LinkedHashMap<String, JBoltEnumBean> tvMap = new LinkedHashMap<String, JBoltEnumBean>();
		int size = texts.size();
		JBoltEnumBean bean;
		for (int i = 0; i < size; i++) {
			bean = new JBoltEnumBean(texts.get(i).toString(), hasValueAttr?values.get(i):names.get(i));
			tvMap.put("v_" + bean.getValue(), bean);
			tvMap.put("t_" + bean.getText(), bean);
		}
		ENUM_TV_BEANMAP.put(clazz.getName(), tvMap);
		ENUM_NAME_CLASSMAP.put(clazz.getSimpleName(), clazz);
		return tvMap;
	}

	/**
	 * 添加到text和value作为key的map中管理
	 *
	 * @param clazz
	 * @return
	 */
	public static LinkedHashMap<String, ? extends Enum<?>> addToVEnumObjectMap(Class<? extends Enum<?>> clazz) {
		return addToVEnumObjectMap(clazz, "text", "value");
	}

	/**
	 * 添加value作为key的map中管理
	 *
	 * @param clazz
	 * @param textAttrName
	 * @param valueAttrName
	 * @return
	 */
	public static LinkedHashMap<String, ? extends Enum<?>> addToVEnumObjectMap(Class<? extends Enum<?>> clazz,
			String textAttrName, String valueAttrName) {
		List<Object> texts = EnumUtil.getFieldValues(clazz, textAttrName);
		if (CollUtil.isEmpty(texts)) {
			return EMPTY_OBJECT_HASHMAP;
		}
		boolean hasValueAttr = valueAttrName!=null&&valueAttrName.trim().length()>0;
		List<Object> values = null;
		List<String> names = null;
		if(hasValueAttr){
			values = EnumUtil.getFieldValues(clazz, valueAttrName);
		}else{
			names = EnumUtil.getNames(clazz);
		}
		LinkedHashMap<String, Enum<?>> emMap = new LinkedHashMap<String, Enum<?>>();
		Enum<?>[] es = clazz.getEnumConstants();
		int esSize = es.length;
		for (int i = 0; i < esSize; i++) {
			emMap.put("v_" + (hasValueAttr?values.get(i):names.get(i)), es[i]);
		}
		ENUM_V_OBJECTMAP.put(clazz.getName(), emMap);
		ENUM_NAME_CLASSMAP.put(clazz.getSimpleName(), clazz);
		return emMap;
	}

	/**
	 * 添加到name作为key的map中管理
	 *
	 * @param clazz
	 * @return
	 */
	public static LinkedHashMap<String, ? extends Enum<?>> addToNameEnumObjectMap(Class<? extends Enum<?>> clazz) {
		Enum<?>[] es = clazz.getEnumConstants();
		if (es == null || es.length == 0) {
			return EMPTY_OBJECT_HASHMAP;
		}
		LinkedHashMap<String, Enum<?>> emMap = new LinkedHashMap<String, Enum<?>>();
		for (Enum<?> e : es) {
			emMap.put(e.name(), e);
		}
		ENUM_NAME_OBJECTMAP.put(clazz.getName(), emMap);
		ENUM_NAME_CLASSMAP.put(clazz.getSimpleName(), clazz);
		return emMap;
	}

	/**
	 * 通过value得到text
	 *
	 * @param clazz
	 * @param value
	 * @return
	 */
	public static <E extends Enum<E>> String getTextByValue(Class<E> clazz, Object value) {
		JBoltEnumBean bean = getByValue(clazz, value);
		return bean == null ? null : bean.getText();
	}

	/**
	 * 通过value得到text
	 *
	 * @param className
	 * @param value
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <E extends Enum<E>> String getTextByValue(String className, Object value) {
		Class<? extends Enum<?>> clazz = ENUM_NAME_CLASSMAP.get(className);
		return getTextByValue((Class<E>) clazz, value);
	}

	/**
	 * 通过text得到得到Integer value
	 *
	 * @param clazz
	 * @param text
	 * @return
	 */
	public static <E extends Enum<E>> Integer getIntegerValueByText(Class<E> clazz, String text) {
		JBoltEnumBean bean = getByText(clazz, text);
		return bean == null ? null : bean.getValueAs();
	}

	/**
	 * 通过text得到int value
	 *
	 * @param clazz
	 * @param text
	 * @return
	 */
	public static <E extends Enum<E>> int getIntValueByText(Class<E> clazz, String text) {
		JBoltEnumBean bean = getByText(clazz, text);
		return bean == null ? -1000 : bean.getValueAs();
	}

	/**
	 * 通过value得到bean
	 *
	 * @param clazz
	 * @param value
	 * @return
	 */
	public static <E extends Enum<E>> JBoltEnumBean getByValue(Class<E> clazz, Object value) {
		if (value == null) {
			return null;
		}
		LinkedHashMap<String, JBoltEnumBean> beanMap = ENUM_TV_BEANMAP.get(clazz.getName());
		if (beanMap == null) {
			beanMap = addToTvBeanMap(clazz);
			if (beanMap == null) {
				return null;
			}
		}
		return beanMap.get("v_" + value);
	}

	/**
	 * 通过value得到Enum对象
	 *
	 * @param clazz
	 * @param value
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Enum<T>> T getEnumObjectByValue(Class<T> clazz, Object value) {
		if (value == null) {
			return null;
		}
		LinkedHashMap<String, ? extends Enum<?>> objMap = ENUM_V_OBJECTMAP.get(clazz.getName());
		if (objMap == null) {
			objMap = addToVEnumObjectMap(clazz);
			if (objMap == null) {
				return null;
			}
		}
		return (T) objMap.get("v_" + value);
	}

	/**
	 * 通过name得到Enum对象
	 *
	 * @param clazz
	 * @param name
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Enum<T>> T getEnumObjectByName(Class<T> clazz, String name) {
		if (StrKit.isBlank(name)) {
			return null;
		}
		LinkedHashMap<String, ? extends Enum<?>> objMap = ENUM_NAME_OBJECTMAP.get(clazz.getName());
		if (objMap == null) {
			objMap = addToNameEnumObjectMap(clazz);
			if (objMap == null) {
				return null;
			}
		}
		return (T) objMap.get(name);
	}

	/**
	 * 通过text得到bean
	 *
	 * @param clazz
	 * @param text
	 * @return
	 */
	public static <E extends Enum<E>> JBoltEnumBean getByText(Class<E> clazz, String text) {
		if (StrKit.isBlank(text)) {
			return null;
		}
		LinkedHashMap<String, JBoltEnumBean> beanMap = ENUM_TV_BEANMAP.get(clazz.getName());
		if (beanMap == null) {
			beanMap = addToTvBeanMap(clazz);
			if (beanMap == null) {
				return null;
			}
		}
		return beanMap.get("t_" + text);
	}

	/**
	 * 获取Enum List数据
	 *
	 * @param clazz
	 * @return
	 */
	public static <E extends Enum<E>> List<JBoltEnumBean> getEnumNameBeanList(Class<E> clazz) {
		return getEnumNameBeanList(clazz, "text", "value");
	}

	/**
	 * 获取Enum List数据
	 *
	 * @param clazz
	 * @param textAttrName
	 * @param valueAttrName
	 * @return
	 */
	public static <E extends Enum<E>> List<JBoltEnumBean> getEnumNameBeanList(Class<E> clazz, String textAttrName,
			String valueAttrName) {
		LinkedHashMap<String, JBoltEnumBean> beanMap = ENUM_NAME_BEANMAP.get(clazz.getName());
		if (beanMap == null) {
			beanMap = addToNameBeanMap(clazz, textAttrName, valueAttrName);
			if (beanMap == null) {
				return Collections.emptyList();
			}
		}
		return new ArrayList<JBoltEnumBean>(beanMap.values());
	}

	/******* 用于enjoy模板引擎 *******/

	private static final LinkedHashMap<String, JBoltEnumBean> EMPTY_BEAN_HASHMAP = new LinkedHashMap<String, JBoltEnumBean>();
	private static final LinkedHashMap<String, Enum<?>> EMPTY_OBJECT_HASHMAP = new LinkedHashMap<String, Enum<?>>();

	public static <E extends Enum<E>> LinkedHashMap<String, JBoltEnumBean> getEnumNameBeanMap(Class<E> clazz) {
		return getEnumNameBeanMap(clazz, "text", "value");
	}

	/**
	 * 得到
	 *
	 * @param <E>
	 * @param clazz
	 * @param textAttrName
	 * @param valueAttrName
	 * @return
	 */
	public static <E extends Enum<E>> LinkedHashMap<String, JBoltEnumBean> getEnumNameBeanMap(Class<E> clazz,
			String textAttrName, String valueAttrName) {
		String className = clazz.getSimpleName();
		LinkedHashMap<String, JBoltEnumBean> nameBeanMap = ENUM_NAME_BEANMAP.get(className);
		if (nameBeanMap == null) {
			nameBeanMap = addToNameBeanMap(clazz, textAttrName, valueAttrName);
		}
		return nameBeanMap;

	}

	/**
	 * 添加用enum name作为key的beanMap
	 *
	 * @param <E>
	 * @param clazz
	 * @return
	 */
	public static <E extends Enum<E>> LinkedHashMap<String, JBoltEnumBean> addToNameBeanMap(Class<E> clazz) {
		return addToNameBeanMap(clazz, "text", "value");
	}

	/**
	 * 添加用enum name作为key的beanMap
	 *
	 * @param <E>
	 * @param clazz
	 * @param textAttrName
	 * @param valueAttrName
	 * @return
	 */
	public static <E extends Enum<E>> LinkedHashMap<String, JBoltEnumBean> addToNameBeanMap(Class<E> clazz,
			String textAttrName, String valueAttrName) {
		List<String> names = EnumUtil.getNames(clazz);
		if (CollUtil.isEmpty(names)) {
			return EMPTY_BEAN_HASHMAP;
		}
		List<Object> texts = EnumUtil.getFieldValues(clazz, textAttrName);
		boolean hasValueAttr = valueAttrName!=null&&valueAttrName.trim().length()>0;
		List<Object> values = null;
		if(hasValueAttr){
			values = EnumUtil.getFieldValues(clazz, valueAttrName);
		}
		// 组合beanMap
		LinkedHashMap<String, JBoltEnumBean> beanMap = new LinkedHashMap<String, JBoltEnumBean>();
		int size = names.size();
		for (int i = 0; i < size; i++) {
			beanMap.put(names.get(i), new JBoltEnumBean(texts.get(i).toString(), hasValueAttr?values.get(i):names.get(i)));
		}
		ENUM_NAME_BEANMAP.put(clazz.getSimpleName(), beanMap);
		ENUM_NAME_CLASSMAP.put(clazz.getSimpleName(), clazz);
		return beanMap;
	}

	/**
	 * 枚举类转换添加到模板引擎中
	 *
	 * @param <E>
	 * @param engine
	 * @param clazz
	 */
	public static <E extends Enum<E>> void addEnjoyEngineShareObject(Engine engine, Class<E> clazz) {
		engine.addSharedObject(clazz.getSimpleName(), getEnumNameBeanMap(clazz));
		ENUM_NAME_CLASSMAP.put(clazz.getSimpleName(), clazz);
	}

	/**
	 * 数据源 下拉列表 radio checkbox等 都可以
	 *
	 * @param <E>
	 * @param clazz
	 * @return
	 */
	public static <E extends Enum<E>> List<JBoltEnumBean> getEnumOptionList(Class<E> clazz) {
		return getEnumNameBeanList(clazz);
	}

	/**
	 * 数据源 下拉列表 radio checkbox等 都可以
	 *
	 * @param <E>
	 * @param clazz
	 * @param textAttrName
	 * @param valueAttrName
	 * @return
	 */
	public static <E extends Enum<E>> List<JBoltEnumBean> getEnumOptionList(Class<E> clazz, String textAttrName,
			String valueAttrName) {
		return getEnumNameBeanList(clazz, textAttrName, valueAttrName);
	}

	/**
	 * 数据源 下拉列表 radio checkbox等 都可以
	 *
	 * @param <E>
	 * @param clazz
	 * @return
	 */
	public static <E extends Enum<E>> List<JBoltEnumBean> getEnumOptionListWithout(Class<E> clazz,
			Object... withoutValues) {
		return getEnumOptionListWithout(clazz, "text", "value", withoutValues);
	}

	/**
	 * 数据源 下拉列表 radio checkbox等 都可以
	 *
	 * @param <E>
	 * @param clazz
	 * @param textAttrName
	 * @param valueAttrName
	 * @return
	 */
	public static <E extends Enum<E>> List<JBoltEnumBean> getEnumOptionListWithout(Class<E> clazz, String textAttrName,
			String valueAttrName, Object... withoutValues) {
		List<JBoltEnumBean> beans = getEnumNameBeanList(clazz, textAttrName, valueAttrName);
		if (beans.size() > 0 && withoutValues != null && withoutValues.length > 0) {
			return beans.stream().filter(bean -> (!ArrayUtil.contains(withoutValues, bean.getValue())))
					.collect(Collectors.toList());
		}
		return beans;
	}

	public static LinkedHashMap<String, JBoltEnumBean> getEnumBeanMap(String className) {
		return ENUM_TV_BEANMAP.get(className);
	}

	public static <E extends Enum<E>> LinkedHashMap<String, JBoltEnumBean> getEnumBeanMap(Class<E> clazz) {
		return getEnumBeanMap(clazz.getName());
	}

	public static List<String> getEnumClassNameList(){
		if(ENUM_NAME_CLASSMAP.size()==0){return null;}
		return ENUM_NAME_CLASSMAP.values().stream().map(key->key.getName()).collect(Collectors.toList());
	}

	public static List<Option> getEnumClassNameOptionList(){
		if(ENUM_NAME_CLASSMAP.size()==0){return null;}
		return ENUM_NAME_CLASSMAP.values().stream().map(key->new OptionBean(key.getName())).collect(Collectors.toList());
	}

}
